using System;
using System.Linq;
using System.Collections.Generic;
using System.Windows.Forms;
using Foreglance.MVP.Presenters;

namespace Foreglance.MVP.Views.Builders
{
    public class ListViewBuilder : IListViewBuilder
    {
        private ListViewItemSelectionChangedEventHandler _listViewItemSelectionChangedDelegate;

        public void Build<TEntity>(IEntityViewPresenter<TEntity> viewPresenter)
        {
            var containerView = ((Control) viewPresenter.View);
            BuildActionButtons(viewPresenter, containerView);
            var listView = BuildListView(viewPresenter, containerView);
            viewPresenter.OnRefreshView += (sender, entities) => SetRows(listView, viewPresenter, entities);
            viewPresenter.OnEntitiesDeleted += (sender, entities) => DeleteRows(listView, entities);
            viewPresenter.OnEntitiesAdded += (sender, entities) => AddRows(listView, viewPresenter, entities);
        }

        private void SetRows<TEntity>(ListView listView, IEntityViewPresenter<TEntity> viewPresenter, IEnumerable<TEntity> entities)
        {
            if (_listViewItemSelectionChangedDelegate != null)
                listView.ItemSelectionChanged -= _listViewItemSelectionChangedDelegate;

            listView.Items.Clear();
            AddRows(listView, viewPresenter, entities);
            
            if (_listViewItemSelectionChangedDelegate != null)
                listView.ItemSelectionChanged += _listViewItemSelectionChangedDelegate;
        }

        private static void AddRows<TEntity>(ListView listView, IEntityViewPresenter<TEntity> viewPresenter, IEnumerable<TEntity> entities)
        {
            var viewProperties = viewPresenter.ViewProperties;
            foreach (var entity in entities)
            {
                var listViewItem = new ListViewItem();
                var entityType = entity.GetType();
                var entityProperties = entityType.GetProperties();
                for (int index = 0; index < viewProperties.Count; index++)
                {
                    var viewProperty = viewProperties[index];
                    var entityProperty = entityProperties.FirstOrDefault(prop => prop.Name == viewProperty.Name);
                    if (entityProperty == null)
                    {
                        throw new Exception(string.Format("Property {0} not found in type {1}", viewProperty.Name,
                                                          entityType));
                    }
                    var value = (entityProperty.GetValue(entity, null) ?? string.Empty).ToString();
                    listViewItem.Tag = entity;
                    if (index == 0)
                    {
                        listViewItem.Text = value;
                    }
                    else
                    {
                        listViewItem.SubItems.Add(value);
                    }
                }
                listView.Items.Add(listViewItem);
            }
        }

        private void DeleteRows<TEntity>(ListView listView, ICollection<TEntity> entities)
        {
            if (_listViewItemSelectionChangedDelegate != null)
                listView.ItemSelectionChanged -= _listViewItemSelectionChangedDelegate;
            listView.Items.Cast<ListViewItem>()
                        .Where(item => entities.Contains((TEntity)item.Tag))
                        .ToList()
                        .ForEach(listView.Items.Remove);
            if (_listViewItemSelectionChangedDelegate != null)
                listView.ItemSelectionChanged += _listViewItemSelectionChangedDelegate;
        }

        private static void BuildActionButtons<TEntity>(IEntityViewPresenter<TEntity> viewPresenter, Control containerView)
        {
            var panel = new Panel{Dock = DockStyle.Bottom, Height = 26};
            containerView.Controls.Add(panel);
            AddButton(panel, viewPresenter, "Add", viewPresenter.AddEntity, viewPresenter.ValidateAddingEnabled);
            AddButton(panel, viewPresenter, "Edit", viewPresenter.EditEntity, viewPresenter.ValidateEditingEnabled);
            AddButton(panel, viewPresenter, "Delete", viewPresenter.DeleteEntities, viewPresenter.ValidateDeletingEnabled);
        }

        private static void AddButton<TEntity>(Panel panel, IEntityViewPresenter<TEntity> viewPresenter, string text, Action action, Func<List<TEntity>, bool> enableFunc)
        {
            var button = new Button { Text = text, Width = 50, Dock = DockStyle.Right};
            panel.Controls.Add(button);
            button.Click += (s, e) => action();
            viewPresenter.OnEntitiesSelectionChanged += (sender, entities) =>
                                                                            {
                                                                                button.Enabled = enableFunc(entities);
                                                                            };
        }

        private ListView BuildListView<TEntity>(IEntityViewPresenter<TEntity> viewPresenter, Control containerView)
        {
            var listView = new ListView
                               {
                                   Dock = DockStyle.Fill,
                                   GridLines = true,
                                   View = System.Windows.Forms.View.Details,
                                   FullRowSelect = true,
                                   HideSelection = false,
                               };
            foreach (var viewProperty in viewPresenter.ViewProperties)
            {
                var columnHeader = new ColumnHeader();
                listView.Columns.Add(columnHeader);
                columnHeader.Text = viewProperty.Text;
            }

            _listViewItemSelectionChangedDelegate = delegate(object s, ListViewItemSelectionChangedEventArgs e)
                                                   {
                                                       if (e.IsSelected)
                                                           viewPresenter.SelectEntity((TEntity) e.Item.Tag);
                                                       else
                                                           viewPresenter.UnselectEntity((TEntity) e.Item.Tag);
                                                   };
            listView.ItemSelectionChanged += _listViewItemSelectionChangedDelegate;
            containerView.Controls.Add(listView);
            return listView;
        }
    }
}